from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.hashers import make_password
from django.db.models import Q
from django.core.paginator import Paginator,EmptyPage
import traceback
from django.utils import timezone
from django.db.models import F
from django.db import transaction,IntegrityError
import json



class User(AbstractUser):
    id = models.BigAutoField(primary_key=True)

    realname = models.CharField(max_length=30,db_index=True)
    studentno = models.CharField(max_length=50,db_index=True,
                                 null=True,blank=True)
    desc = models.CharField(max_length=50)
    TYPECHOICES = [
        (1,'管理员'),
        (2000,'学生'),
        (3000,'老师')
    ]
    usertype = models.SmallIntegerField(choices=TYPECHOICES)

    REQUIRED_FIELDS = ['realname','usertype']

    class Meta:
        db_table = 'cimp_user'

    @classmethod
    def listbypage(cls,pagesize,pagenum,keywords,usertype=None):
        try:
            values_list = ['id','username','realname','studentno','desc','usertype']
            qs = cls.objects.values(*values_list).order_by('-id')

            if usertype:
                qs = qs.filter(usertype=usertype)

            if keywords:
                conditions = [Q(usernamr__contains=one) for one in keywords.split(' ') if one]

                query = Q()
                for condition in conditions:
                    query &= condition

                qs = qs.filter(query)

            pgnt = Paginator(qs,pagesize)
            page = pgnt.page(pagenum)

            retlist = list(page)

            return {'ret': 0, 'items': retlist, 'total': pgnt.count,'keywords':keywords}

        except EmptyPage:
            return {'ret': 0, 'items': [], 'total': 0, 'keywords': keywords}

        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}

    @classmethod
    def addone(cls,data):


        try:
            user = cls.objects.create(
                realname=data['realname'],
                username = data['username'],
                password = make_password(data['password']),
                studentno = data['studentno'],
                usertype = data['usertype'])

            return {'ret':0, 'id': user.id}

        except:
            err = traceback.format_exc()
            return {'ret':2,'msg':err}

    @classmethod
    def modifyone(cls,oid,newdata):
        try:
            user = cls.objects.get(id=oid)

            if 'username' in newdata:
                username = newdata['username']
                if cls.objects.filter(username=username).exists():
                    return {'ret': 3, 'msg': f'登录名 为 {username} 的用户已经存在'}

            if 'password' in newdata:
                user.password = make_password(newdata.pop('password'))

            for name,value in newdata.items():
                setattr(user,name,value)

            user.save()
            return {'ret':0}

        except cls.DoseNotExist:
            return {'ret':1, 'msg': f'id 为 {oid} 的用户不存在'}
        except :
            err = traceback.format_exc()
            return {'ret':2, 'msg': err}


    @classmethod
    def deleteone(cls,oid):
        try:
            user = cls.objects.get(id=oid)
            user.delete()
            return {'ret':0}

        except cls.DoseNotExist:
            return {'ret':1,'msg': f'id 为 {oid} 的用户不存在'}
        except:
            err = traceback.format_exc()
            return {'ret':2, 'msg': err}

    @classmethod
    def getmyprofile(cls,oid):
        try:
            user = cls.objects.get(id=oid)
            rec = {
                    "ret": 0,
                    "profile": {
                    "userid": user.id,
                    "username": user.username,
                    "usertype": user.usertype,
                    "realname": user.realname
                    }
                }

            return rec

        except cls.DoseNotExist:
            return {'ret': 1, 'msg': f'id 为 {oid} 的用户不存在'}
        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}





class Article(models.Model):
    id = models.BigAutoField(primary_key=True)
    pubdate = models.DateTimeField(default=timezone.now,db_index=True)
    author = models.ForeignKey(User,on_delete=models.PROTECT,db_index=True)
    title = models.CharField(max_length=100,db_index=True)
    content = models.TextField(max_length=50000,null=True,blank=True)

    STATUS_CHOICES = [
        (1,'发布状态'),
        (2,'撤回状态'),
        (3,'封禁状态')
    ]

    status = models.SmallIntegerField(choices=STATUS_CHOICES,default=1,db_index=True)

    STATUS_PUBLISH = 1
    STATUS_HOLD = 2
    STATUS_BAN = 3

    VALUE_LIST = ['id', 'pubdate', 'author', 'author__realname', 'title', 'content', 'status']

    class Meta:
        abstract = True

    @classmethod
    def listbypage(cls,pagesize,pagenum,keywords,
                    withoutContent=False,onlypublish=False,
                   authorid=False):
        try:
            value_list = cls.VALUE_LIST
            if withoutContent:
                value_list.remove('content')

            qs = cls.objects.values(*value_list).order_by('-id')

            if onlypublish:
                qs = qs.filter(status=cls.STATUS_PUBLISH)

            if authorid:
                qs = qs.filter(author_id=authorid)

            if keywords:
                conditions = [Q(title__contains=one) for one in keywords.split(' ') if one]
                query = Q()
                for condition in conditions:
                    query &= condition

                qs = qs.filter(query)

            pgnt = Paginator(qs,pagesize)
            page = pgnt.page(pagenum)

            retlist = list(page)

            return {'ret':0,"items":retlist,
                    "total": qs.count(),"keywords": keywords}

        except EmptyPage:
            return {'ret':0,"items":[],
                    "total": 0,"keywords": keywords}
        except:
            err = traceback.format_exc()
            return {'ret':1,'msg':err}

    @classmethod
    def getone(cls,oid):
        vlaue_list = cls.VALUE_LIST

        try:
            qs = cls.objects.filter(id=oid).values(*vlaue_list)

            if qs.count() == 0:
                return {
                    'ret': 1,
                    'msg': f'id 为`{oid}`的记录不存在'
                }

            rec = list(qs)[0]

            return {'ret':0,'rec':rec}
        except:
            err = traceback.format_exc()
            return {'ret':1,'msg':err}

    @classmethod
    def addone(cls,data,uid):
        title = data.get('title')
        content = data.get('content')

        try:
            rec = cls.objects.create(
                            author_id = uid,
                            title=title,
                            content=content)

            return {"ret": 0,"id": rec.id}
        except:
            err = traceback.format_exc()
            return {'ret':1,'msg':err}

    @classmethod
    def modifyone(cls,oid,newdata,authorid=False):
        try:
            rec = cls.objects.get(id=oid)
            if authorid:
                if rec.author_id != authorid:
                    return {'ret': 1, 'msg': '该功能仅对论文作者开放'}

            for name,value in newdata.items():
                setattr(rec,name,value)

            rec.save()

            return {'ret':0}

        except cls.DoseNotExist:
            return {'ret': 1, 'msg': f'id 为 {oid} 的记录不存在'}
        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}

    @classmethod
    def banone(cls,oid):
        try:
            rec = cls.objects.get(id=oid)
            rec.status = cls.STATUS_BAN

            rec.save()

            return {'ret': 0,'status':cls.STATUS_BAN}

        except cls.DoseNotExist:
            return {'ret': 1, 'msg': f'id 为 {oid} 的记录不存在'}
        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}

    @classmethod
    def publishone(cls,oid,usertype=False,authorid=False):
        try:
            rec = cls.objects.get(id=oid)

            if usertype or authorid:
                if usertype != 1 and authorid != rec.author_id:
                    return {'ret': 1, 'msg': '该功能仅对论文作者与管理员开放'}

            rec.status = cls.STATUS_PUBLISH

            rec.save()

            return {'ret': 0,"status":cls.STATUS_PUBLISH}

        except cls.DoseNotExist:
            return {'ret': 1, 'msg': f'id 为 {oid} 的记录不存在'}
        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}

    @classmethod
    def deleteone(cls,oid,usertype=False,authorid=False):
        try:
            rec = cls.objects.get(id=oid)
            if usertype or authorid:
                if usertype != 1 and authorid != rec.author_id:
                    return {'ret': 1, 'msg': '该功能仅对论文作者与管理员开放'}

            rec.delete()

            return {'ret': 0}

        except cls.DoseNotExist:
            return {'ret': 1, 'msg': f'id 为 {oid} 的记录不存在'}
        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}

    @classmethod
    def holdone(cls,oid,authorid):
        rec = cls.objects.get(id=oid)
        if rec.author_id != authorid:
            return {'ret': 1, 'msg': '不可撤回他人的劳动成果'}

        rec.status = 2
        rec.save()

        return {"ret": 0,"status": 2}

    @classmethod
    def getmany(cls,idlist):
        selectlist = ['id','pubdate','author','author__realname','title','status']
        try:
            rec = cls.objects.filter(id__in=idlist).values(*selectlist)
            return list(rec)

        except:
            err = traceback.format_exc()
            return {'ret': 1, 'msg': err}


class Notice(Article):
    class Meta:
        db_table = 'cimp_notice'

class News(Article):
    class Meta:
        db_table = 'cimp_news'

class Paper(Article):

    thumbupcount = models.IntegerField(default=0)
    VALUE_LIST = ['id', 'pubdate', 'author', 'author__realname', 'title', 'content', 'thumbupcount', 'status']

    class Meta:
        db_table = 'cimp_paper'

class Config(models.Model):
    # 配置项名称，目前主要是 homepage
    name = models.CharField(max_length=100, unique=True)
    # 内容，以json格式存储数据
    value = models.TextField(max_length=50000, null=True, default=None)

    class Meta:
        db_table = "cimp_config"

    @classmethod
    def setconfig(cls,name,value):
        try:
            cls.objects.update_or_create(
                name=name,
                defaults={'value':value}
            )

            return {'ret':0}
        except:
            err = traceback.format_exc()
            return {'ret':1,'msg':err}

    @classmethod
    def getconfig(cls,name):
        try:
            rec = cls.objects.get(name=name)
            return {'ret':0,'value':rec.value}

        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}

class TSRelation(models.Model):
    student = models.ForeignKey(User, on_delete=models.CASCADE,related_name='myteachers')
    teacher = models.ForeignKey(User, on_delete=models.CASCADE,related_name='mystudents')

    class Meta:
        db_table = "cimp_studentteacherrelation"

    @classmethod
    def gerteacher(cls,oid):
        try:
            rela = cls.objects.get(student_id=oid)
            return {'id':rela.teacher_id,'realname':rela.teacher.realname}
        except:
            return {'id': -1, 'realname': '尚未设置'}

    @classmethod
    def setrelation(cls,oid,teacherid):
        try:
            cls.objects.update_or_create(
                student_id=oid,
                defaults={'teacher_id':teacherid}
            )

            return {'ret': 0}
        except:
            err = traceback.format_exc()
            return {'ret':1,'msg':err}


class ThumbupManage(models.Model):
    id = models.BigAutoField(primary_key=True)
    user = models.ForeignKey(User,on_delete=models.CASCADE)
    thumbupPapers = models.TextField(default=',')

    class Meta:
        db_table = 'cimp_userpaperthumbup'


class ThumbupOrCancel:

    @staticmethod
    def thumbuporcancel(userid,paperid):
        thu = ThumbupManage.objects.filter(user_id=userid).first()

        if thu is None:
            thu = ThumbupManage.objects.create(user_id=userid)

        thumbuppapers = thu.thumbupPapers
        papertext = f',{paperid},'


        paper = Paper.objects.filter(id=paperid).first()
        if paper is None:
            return {'ret':1,'msg':f'id为{paperid}的论文不存在'}

        if papertext in thumbuppapers:
            thumbuppapers = thumbuppapers.replace(papertext,',')
            thu.thumbupPapers = thumbuppapers
            paper.thumbupcount -= 1
        else:
            thumbuppapers = f',{paperid}' + thumbuppapers
            thu.thumbupPapers = thumbuppapers
            paper.thumbupcount += 1

        try:
            with transaction.atomic():
                thu.save()
                paper.save()
                return {'ret':0,'thumbupcount':paper.thumbupcount}
        except IntegrityError:
            return {'ret':5,'msg':'数据库操作失败'}


class WF(models.Model):

    id = models.BigAutoField(primary_key=True)
    creator = models.ForeignKey(User,on_delete=models.PROTECT)
    title = models.CharField(max_length=50,db_index=True)
    currentstate = models.CharField(max_length=50)
    createdate = models.DateTimeField(default=timezone.now,db_index=True)

    class Meta:
        abstract = True


class WFStep(models.Model):
    id = models.BigAutoField(primary_key=True)
    operator = models.ForeignKey(User,on_delete=models.PROTECT)
    actiondate = models.DateTimeField(default=timezone.now, db_index=True)
    actionname = models.CharField(max_length=50)
    actiondata = models.TextField(default='')
    nextstate = models.CharField(max_length=50)

    class Meta:
        abstract = True

class WFDataMgr():

    LISTBYPAGE_VALUE_LIST = ['id','creator','creator__realname','title','currentstate','createdate']
    GETONESTEP_VALUE_LIST = ['id','operator__realname','actiondate','actionname','nextstate']

    @classmethod
    def listbypage(cls,pagesize,pagenum,keywords,creators=None):
        try:
            qs = cls.WF_MODEL.objects.values(*cls.LISTBYPAGE_VALUE_LIST).order_by('id')

            if creators:
                qs = qs.filter(creator_id__in=creators)

            if keywords:
                conditions = [Q(title_contains=one) for one in keywords.split(' ') if one]
                query = Q()
                for condition in conditions:
                    query &= condition
                qs = qs.filter(query)

            pgnt = Paginator(qs,pagesize)
            page = pgnt.page(pagenum)
            retlist = list(page)

            return {'ret':0,'items':retlist,'total':pgnt.count,'keywords':keywords}

        except EmptyPage:
            return {'ret': 0, 'items': [], 'total': 0, 'keywords': cls.keywords}
        except:
            err = traceback.format_exc()
            return {'ret': 2, 'msg': err}


    @classmethod
    def getone(cls,wf_id,getsteps=True):
        try:
            wf = cls.WF_MODEL.objects.get(id=wf_id)
            rec = {
                'id':wf.id,
                'creatorname':wf.creator.username,
                'title':wf.title,
                'currentstate':wf.currentstate,
                'createdate':wf.createdate}

            if getsteps:
                qs_steps = cls.WFStep_MODEL.objects.filter(wf_id=wf.id).values(*cls.GETONESTEP_VALUE_LIST)
                steps = list(qs_steps)
                rec['steps'] = steps

            return {'ret': 0, 'rec': rec, 'wf':wf}
        except:
            err = traceback.format_exc()
            return {'ret': 1, 'msg': err}


    @classmethod
    def create(cls,uid,title,currentstate,actiondata,nextstate):
        with transaction.atomic():
            wf = cls.WF_MODEL.objects.create(creator_id=uid,
                                              title=title,
                                              currentstate=nextstate)

            cls.WFStep_MODEL.objects.create(operator_id=uid,
                                            actionname=currentstate,
                                            actiondata=actiondata,
                                            nextstate=nextstate,
                                            wf=wf)

            return wf

    @classmethod
    def update(cls,wf_id,uid,title,actionname,actiondata,nextstate):
        with transaction.atomic():
            wf = cls.WF_MODEL.objects.get(id=wf_id)
            wf.currentstate = nextstate
            if title is not None:
                wf.title = title
            wf.save()

            cls.WFStep_MODEL.objects.create(operator_id=uid,
                                            actionname=actionname,
                                            actiondata=actiondata,
                                            nextstate=nextstate,
                                            wf_id=wf_id)

        return wf

    @classmethod
    def getstepactiondata(cls,step_id):
        rec = cls.WFStep_MODEL.objects.get(id=step_id)

        data = json.loads(rec.actiondata)

        return {'ret':0, 'data':data}


class WF_Design(WF):
    class Meta:
        db_table = 'cimp_wf_design'


class WF_Design_STEP(WFStep):
    wf = models.ForeignKey(WF_Design,on_delete=models.PROTECT,
                           related_name='step')

class WF_Design_DataMgr(WFDataMgr):
    WF_MODEL = WF_Design
    WFStep_MODEL = WF_Design_STEP































